#include "libcomm.h"

ThreadSemaphore::ThreadSemaphore(unsigned int nInitValue)
{
#ifdef COMM_MSVC
	MYLIB_ASSERT_ERRNO(NULL != (m_sem = CreateSemaphore(NULL,nInitValue,SEM_VALUE_MAX,NULL)));
#else
	MYLIB_ASSERT_ERRNO(0 == sem_init(&m_sem, 0, nInitValue));
#endif
}

ThreadSemaphore::~ThreadSemaphore(void)
{
#ifdef COMM_MSVC
	if(NULL != m_sem)
	{
		CloseHandle(m_sem);
		m_sem = NULL;
	}
#else
	MYLIB_ASSERT_ERRNO(0 == sem_destroy(&m_sem));
#endif
}

void ThreadSemaphore::SetSignal()
{
#ifdef COMM_MSVC
	MYLIB_ASSERT_ERRNO(ReleaseSemaphore(m_sem,1,NULL));
#else
	MYLIB_ASSERT_ERRNO(0 == sem_post(&m_sem));
#endif
}

#ifdef __GNUC__
static void __wait_cleanup(void *arg)
{    
	LOGWARN("thread(%d) was canceled while ThreadSemaphore::WaitSignal().",Thread::Id());
}
#endif

bool ThreadSemaphore::WaitSignal(unsigned int timeoutMs)
{
#ifdef COMM_MSVC

	DWORD dwEvent = WaitForSingleObject(m_sem,timeoutMs); 
	switch (dwEvent) 
	{ 
	case WAIT_OBJECT_0: 
			return true;
	case WAIT_TIMEOUT: 
			return false;
	default: 
		break;
	}
	LOGFUNCERROR(WaitForSingleObject);
	return false;

#else

	int ret = 0;
	timespec ts = { 0 };
	if (timeoutMs > 0)
		ts = Timer::MakeTimespec(timeoutMs);
	pthread_cleanup_push(__wait_cleanup, NULL);
	while(true)
	{
		if (-1 == timeoutMs)
		{
			ret = sem_wait(&m_sem);
		}
		else if (0 == timeoutMs)
		{
			ret = sem_trywait(&m_sem);
		}
		else
		{
			ret = sem_timedwait(&m_sem, &ts);
		}
	
		if (0 != ret && EINTR == errno)
		{
			LOGFUNCERROR(sem_timedwait);
			continue;
		}
		break;
	}
	pthread_cleanup_pop(0);

	if(0 == ret)
		return true;
	if(ETIMEDOUT == errno || EAGAIN == errno)
		return false;

	LOGFUNCERROR(sem_timedwait);
	return false;

#endif

}
